#pragma once

template<class T>
class CircuitQueue
{
public:
	CircuitQueue() : m_pData(NULL), m_nLength(0), m_nSize(0), m_nHead(0), m_nTail(0) 
	{

	}
	~CircuitQueue()
	{
		if (m_pData)
		{
			delete [] m_pData;
			m_pData = NULL;
		}
	}

	void Create(size_t nSize, size_t nExtraSize)
	{
		if (m_pData)
		{
			delete [] m_pData;
			m_pData = NULL;
		}

		m_pData = new T[nSize + nExtraSize];
		m_nSize = nSize;
		m_nExtraSize = nExtraSize;
	}

	size_t GetSpace()
	{
		size_t nRet = 0;

		nRet = m_nSize - m_nLength;

		return nRet;
	}

	size_t GetLength()
	{
		size_t nRet = 0;

		nRet = m_nLength;

		return nRet;
	}

	size_t GetSize()
	{
		return m_nSize;
	}

	T* GetReadPtr(size_t& nLen)
	{
		T* pRet = NULL;
		size_t nRet = 0;

		pRet = m_pData + m_nHead;
		if (m_nHead == m_nTail)
		{
			nRet = GetLength() > 0 ? m_nSize - m_nHead : 0;
		}
		else if(m_nHead < m_nTail)
		{
			nRet = m_nTail - m_nHead;
		}
		else
		{
			nRet = m_nSize - m_nHead;
		}

		nLen = nRet;
		return pRet;
	}

	T* GetWritePtr(size_t& nLen)
	{

		T* pRet = NULL;

		pRet = m_pData + m_nTail;
		if (m_nHead == m_nTail)
		{
			nLen = GetLength() > 0 ? 0 : m_nSize - m_nTail;
		}
		else if (m_nHead < m_nTail)
		{
			nLen = m_nSize - m_nTail;
		}
		else
		{
			nLen = m_nHead - m_nTail;
		}

		return pRet;
	}

	bool Enqueue(T* pSrc, size_t nLen)
	{

		if (GetSpace() < nLen)
			return false;

		if (pSrc)
		{
			if (m_nHead <= m_nTail)
			{
				size_t nBackSpaceCount = m_nSize - m_nTail;
				if (nBackSpaceCount < nLen)
				{
					memcpy(m_pData + m_nTail, pSrc, sizeof(T)*nBackSpaceCount);
					memcpy(m_pData, pSrc + nBackSpaceCount, sizeof(T)*(nLen - nBackSpaceCount));
				}
				else
				{
					memcpy(m_pData + m_nTail, pSrc, sizeof(T) * nLen);
				}
			}
			else
			{
				memcpy(m_pData + m_nTail, pSrc, sizeof(T) * nLen);
			}
		}

		m_nTail += nLen;
		m_nTail %= m_nSize;
		m_nLength += nLen;
		return true;
	}

	bool Peek(T* pTar, size_t nLen)
	{
		if (m_nLength < nLen)
			return false;

		if (pTar)
		{
			if (m_nHead < m_nTail)
			{
				memcpy(pTar, m_pData + m_nHead, sizeof(T) * nLen);
			}
			else
			{
				size_t nBackSpaceCount = m_nSize - m_nHead;
				if (nBackSpaceCount >= nLen)
				{
					memcpy(pTar, m_pData + m_nHead, sizeof(T) * nLen);
				}
				else
				{
					memcpy(pTar, m_pData + m_nHead, sizeof(T) * nBackSpaceCount);
					memcpy(pTar + nBackSpaceCount, m_pData, sizeof(T) * (nLen - nBackSpaceCount));
				}
			}
		}

		return true;
	}

	bool Dequeue(T* pTar, size_t nLen)
	{

		if (Peek(pTar, nLen) == false)
			return false;

		m_nHead += nLen;
		m_nHead %= m_nSize;
		m_nLength -= nLen;
		return true;
	}

	size_t GetBackDataCount()
	{
		size_t nRet;

		nRet = m_nSize - m_nHead;

		return nRet;
	}

	void CopyHeadDataToExtraBuffer(size_t nSize)
	{

		memcpy(m_pData + m_nSize, m_pData, nSize);
	}
private:
	T* m_pData;
	size_t m_nLength;
	size_t m_nSize;
	size_t m_nHead;
	size_t m_nTail;
	size_t m_nExtraSize;
};